package cn.zvo.websocket.framework.springboot.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.xnx3.CacheUtil;
import com.xnx3.Log;
import cn.zvo.websocket.framework.springboot.enums.MessageTypeEnum;
import cn.zvo.websocket.framework.springboot.vo.bean.Message;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import net.sf.json.JSONObject;

/**
 * Socket相关操作
 * @author 管雷鸣
 *
 */
public class SocketUtil {
	/**
	 * kefu:channelBind:channelId:{channelId} 根据channelId取 用户的唯一ID标识
	 */
	public static final String EXCHANGE_BIND_CHATID = "kefu:channelBind:{channelId}:chatid";
	public static Map<String, List<Channel>> channelMap;	//key: user.chatid   value: List<Channel> 因为一个user可能会在多个设备登录，也就是存在多个通道
	static{
		channelMap = new HashMap<String, List<Channel>>();
	}
	
	/**
	 * 根据通道，获取通道的id
	 * @param channel 
	 * @return 通道id， channel.id().asShortText();
	 */
	public static String getChannelId(Channel channel){
		return channel.id().asShortText();
	}
	
	/**
	 * 将socket消息通道跟用户绑定
	 * @param user {@link User}无论是已登录用户还是游客，都可以绑定
	 * @param channel {@link Channel}消息通道
	 */
	public static void bind(String chatid, Channel channel){
		if(chatid == null || chatid.length() == 0){
			return;
		}
		String channelid = getChannelId(channel);
		
		List<Channel> list = getChannel(chatid);
		for (int i = 0; i < list.size(); i++) {
			Channel chan = list.get(i);
			if(channelid.equals(getChannelId(chan))){
				//相同，那么就不需要绑定，已经绑定过了
				return;
			}
		}
		//既然没有在for中执行return退出，那么就还没绑定过，进行绑定
		list.add(channel);
		//根据userid可以取channel 列表
		channelMap.put(chatid, list);
		
		//根据channelId取chatid
		CacheUtil.setYearCache(EXCHANGE_BIND_CHATID.replace("{channelId}", channelid), chatid);
	}

	/**
	 * 根据 chatid 获取websocket通道
	 * @param chatid user.chatid（原本是 {@link User}.id ，也是对话窗口用户的id）
	 * @return 对应的通道。如果没有对应的通道，那返回 new ArrayList();
	 */
	public static List<Channel> getChannel(String chatid){
		List<Channel> list = channelMap.get(chatid);
		if(list == null){
			return new ArrayList<Channel>();
		}
		return list;
	}
	
	/**
	 * 判断某个用户当前是否在线
	 * @param chatid 用户的唯一标识
	 * @return true 在线
	 */
	public static boolean isOnline(String chatid) {
		List<Channel> list = getChannel(chatid);
		return list.size() > 0;
	}
	
	/**
	 * 根据通道获取用户 chatid
	 * @param channel 通道。这个通道不一定还存在，因为重启服务器后，通道就释放了。但是通道绑定的user是在redis保存的，所以依旧能根据不存在的通道，取到这个通道属于哪个用户
	 * @return 对应的用户 chatid ，如果没有，那返回null
	 */
	public static String getChatid(Channel channel){
		Object obj = CacheUtil.get(EXCHANGE_BIND_CHATID.replace("{channelId}", getChannelId(channel)));
		if(obj == null){
			return null;
		}
		String chatid = (String) obj;
		return chatid;
	}
	
	/**
	 * 通道解除绑定，用户某个通道下线时触发
	 */
	public static void unbind(Channel channel){
		String chatid = getChatid(channel);
		String channelId = getChannelId(channel);
		
		//删除根据channelid获取user信息的绑定
		CacheUtil.delete(EXCHANGE_BIND_CHATID.replace("{channelId}", channelId));
		Log.log("channel unbind -- , channelId:"+channelId+", chatid:"+chatid);
		
		//删除根据userid 获取 Channel列表中的这个channel。 一个user账号可能在多个设备使用，有多个channel，这里只是删除传入的这个channel
		int index = -1;	//要删除的list的下标
		List<Channel> list = getChannel(chatid);
		for (int i = 0; i < list.size(); i++) {
			Channel chan = list.get(i);
			if(getChannelId(channel).equals(getChannelId(chan))){
				//相同，记下下标
				index = i;
				break;
			}
		}
		//既然没有退出，那么就还没绑定过，进行绑定
		if(index > -1){
			//有要删除的，那么删除
			list.remove(index);
		}
		//更新缓存
		if(list.size() == 0){
			//如果这个用户已经没有存活通道了，那么直接删除掉对应关系
			channelMap.remove(chatid);
		}else{
			channelMap.put(chatid, list);
		}
	}
	
	/**
	 * 获取当前在线终端 (chatid) 列表
	 */
	public static List<String> getOnline(){
		List<String> list = new ArrayList<String>();
		for(Map.Entry<String, List<Channel>> entry : channelMap.entrySet()){
			list.add(entry.getKey());
		}
		return list;
	}

    /**
     * 发送一条系统消息
     * @param sendId 消息发送者id，其实系统消息的发送者是系统。比如A给B发送消息，B离线了，那么A会收到对方已经离线的系统提示，那这个对方已经离线的消息提示的发送者便是B
     * @param receiveId 消息接受者id
     * @param text 系统消息的内容
     */
    public static void sendSystemMessage(String sendId, String receiveId, String text){
    	JSONObject json = new JSONObject();
    	json.put("receiveId", receiveId);
    	json.put("sendId", sendId);
    	json.put("type", MessageTypeEnum.SYSTEM.name);
    	json.put("text", text);
    	
    	sendMessage(receiveId, json.toString());
    }
    
    /**
     * 发送心跳消息回复。 也就是客户端发送信条消息，消息text为 AreYouThere ,那么服务端要给客户端回复我还在
     * @param channel 当前消息通道
     */
    public static void sendHeartbeatMessageReply(Channel channel){
    	JSONObject json = new JSONObject();
    	json.put("type", MessageTypeEnum.HEARTBEAT.name);
    	json.put("text", "yes");
    	channel.writeAndFlush(new TextWebSocketFrame(json.toString()));
    }
    
    /**
     * 给某个指定用户发送一条消息
     * @param message {@link Message}要发送出去的消息
     */
    public static void sendMessage(Channel channel,Message message){
    	//通过通道，获取这个通道是哪个用户的
//    	ChatUserBean chatUser = getUser(channel);
//    	if(chatUser == null){
//    		//用户不存在
//    		ConsoleUtil.error("发送消息时，根据channel获取这个通道绑定的用户，发现没有绑定的用户。");
//    		return;
//    	}
    	if(channel == null || message == null) {
    		return;
    	}
		String messageString = JSONObject.fromObject(message).toString();
		sendMessage(channel, messageString);
    }
    
    /**
     * 给某个指定用户发送一条消息
     * @param message {@link Message}要发送出去的消息
     */
    public static void sendMessage(Channel channel,String message){
		channel.writeAndFlush(new TextWebSocketFrame(message));
    }
    
    /**
     * 给某个指定用户发送一条消息
     * @param chatid user.chatid（旧的是 userid 接受者的用户id，消息发送给这个用户）
     * @param msg 消息内容，socket发送出去的内容
     */
    public static void sendMessage(String chatid, String msg){
    	List<Channel> channelList = getChannel(chatid);
    	sendMessage(channelList, msg);
    }
    
    /**
     * 给某个指定用户发送一条消息
     * @param chatid 用户的唯一标识
     * @param msg 消息内容，socket发送出去的内容
     */
    public static void sendMessage(String chatid,Message message){
    	List<Channel> channelList = getChannel(chatid);
    	sendMessage(channelList, JSONObject.fromObject(message).toString());
    }

    /**
     * 给指定的几个通道发送一条消息
     * @param channelList 接受者的消息通道，消息发送给这个消息通道的用户
     * @param msg 消息内容，socket发送出去的内容
     */
    public static void sendMessage(List<Channel> channelList, String msg){
//    	ConsoleUtil.debug("socket send message : "+msg);
    	for (int i = 0; i < channelList.size(); i++) {
    		sendMessage(channelList.get(i), msg);
		}
    }
    
}
